//@author A0097091Y
//This class provides functionalities for storing of tasks

package crossoff.db;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import crossoff.datetime.DateTimeTypeConverter;
import crossoff.model.Task;


public class Storage {

	private static Gson gson;
	private static File taskFile;
	private static final String TASK_FILE = "crossofftasks.json";
	private static Writer writer;
	private static BufferedReader reader;
	public ArrayList<Task> taskCache;
	
	private static Storage store;

	private Storage() throws IOException {
		gson = initialiseGson();
		initialiseFiles();
	}
	
	public static Storage getInstance() throws IOException {
		if (store == null) {
			store = new Storage();
		}
		
		return store;
	}

	private void initialiseFiles() throws IOException {

		taskFile = new File(TASK_FILE);

		taskFile.createNewFile();
		reader = new BufferedReader(new FileReader(TASK_FILE));

		taskCache = gson.fromJson(reader, new TypeToken<ArrayList<Task>>() {}.getType());
		if (taskCache == null) {
			taskCache = new ArrayList<Task>();
		}

	}

	private Gson initialiseGson() {

		GsonBuilder builder = new GsonBuilder();
		builder.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter());

		return builder.setPrettyPrinting().create();
	}

	public Task saveFromTimedSingleDay(String inputDescription,
			LocalTime finalStart, LocalTime finalEnd, LocalDate finalDay)
			throws IOException {


		DateTime inputStart = new DateTime(finalDay.getYear(),
				finalDay.getMonthOfYear(), finalDay.getDayOfMonth(),
				finalStart.getHourOfDay(), finalStart.getMinuteOfHour());
		DateTime inputEnd = new DateTime(finalDay.getYear(),
				finalDay.getMonthOfYear(), finalDay.getDayOfMonth(),
				finalEnd.getHourOfDay(), finalEnd.getMinuteOfHour());

		Task toSave = new Task(inputDescription, inputStart, inputEnd);

		addTaskToFile(toSave);
		
		return toSave;

	}

	public Task saveFromTimedMutipleDay(String inputDescription,
			LocalTime finalStart, LocalTime finalEnd, LocalDate StartDay,
			LocalDate finalDay) throws IOException {


		DateTime inputStart = new DateTime(StartDay.getYear(),
				StartDay.getMonthOfYear(), StartDay.getDayOfMonth(),
				finalStart.getHourOfDay(), finalStart.getMinuteOfHour());
		DateTime inputEnd = new DateTime(finalDay.getYear(),
				finalDay.getMonthOfYear(), finalDay.getDayOfMonth(),
				finalEnd.getHourOfDay(), finalEnd.getMinuteOfHour());

		Task toSave = new Task(inputDescription, inputStart, inputEnd);



		addTaskToFile(toSave);
		return toSave;

	}

	public Task saveFromTimed(String inputDescription,
			DateTime finalStart, DateTime finalEnd) throws IOException {

		Task toSave = new Task(inputDescription, finalStart, finalEnd);
		addTaskToFile(toSave);
		
		return toSave;
	}

	public Task saveAtTimed(String description, LocalTime start,
			LocalDate finalDay) throws IOException {

		DateTime inputStart = new DateTime(finalDay.getYear(),
				finalDay.getMonthOfYear(), finalDay.getDayOfMonth(),
				start.getHourOfDay(), start.getMinuteOfHour());
		Task toSave = new Task(description, inputStart);
		addTaskToFile(toSave);
		
		return toSave;

	}

	public Task saveDeadline(String description, LocalTime end,
			LocalDate day) throws IOException {

		DateTime inputEnd = new DateTime(day.getYear(), day.getMonthOfYear(),
				day.getDayOfMonth(), end.getHourOfDay(), end.getMinuteOfHour());

		Task toSave = new Task(description, null, inputEnd);
		addTaskToFile(toSave);
		
		return toSave;

	}

	public void addTaskToFile(Task toSave) throws IOException {

		writer = new FileWriter(taskFile);
		taskCache.add(toSave);
		gson.toJson(taskCache, writer);
		writer.close();
		reader.close();
	}

	public Task saveFloating(String description) throws IOException {

		Task toSave = new Task(description);
		addTaskToFile(toSave);
		return toSave;

	}

	public boolean deleteTask(int taskIDToDelete) throws IOException {

		for (Task t : taskCache) {
			if (t.getTaskID() == taskIDToDelete) {
				taskCache.remove(t);
				refreshFile();
				return true;
			}
		}

		return false;

	}
	
	public boolean deleteTask(Task toDelete) throws IOException {

		if (taskCache.remove(toDelete)) {

			return true;
		}
		
		else {
			return false;
		}

	}

	private void refreshFile() throws IOException {
	
		writer = new FileWriter(taskFile);
		gson.toJson(taskCache, writer);
		writer.close();
		reader.close();
	
	}

	public String fetchDescription(int taskIDToFetch) {
	
		for (Task t : taskCache) {
			if (t.getTaskID() == taskIDToFetch) {
				return t.getDescription();
			}
		}
	
		return null;
	}

	public Task getTaskFromID(int taskID) {
		for (Task t : taskCache) {
			if (t.getTaskID() == taskID) {
				return t;
			}
		}
		
		return null;
	}

	//@author A0091838U
	
	/**
	 * Generates the string that is displayed to user for editing based on a Task details
	 * @param taskIDToEdit
	 * @return String that is displayed to user for editing
	 * @throws IOException
	 */
	public String getEditDescription(int taskIDToEdit) throws IOException {


		for (Task t : taskCache) {
			if (t.getTaskID() == taskIDToEdit) {
				if (t.getStart() != null && t.getEnd() != null) {
					return getFromTimedDescription(t);
				}
				if (t.getEnd() != null) {
					return getDeadlineDescription(t);
				}
				if (t.getStart() != null) {
					return getAtTimedDescription(t);
				} else {
					return getFloatingDescription(t);
				}
			}
		}

		return null;

	}

	private String getFloatingDescription(Task t) {
		String discription;
		String task_to_edit;
		discription = t.getDescription();
		task_to_edit = discription;
		return task_to_edit;
	}

	private String getAtTimedDescription(Task t) {
		String discription;
		String task_to_edit;
		int start_day;
		int start_month;
		int start_year;
		int start_minutes;
		int start_hours;
		String start_minutesStr;
		start_day = t.getStart().getDayOfMonth();
		start_month = t.getStart().getMonthOfYear();
		start_year = t.getStart().getYear();
		start_minutes = t.getStart().getMinuteOfHour();
		if (start_minutes <= 9) {
			start_minutesStr = "0" + start_minutes;
		} else {
			start_minutesStr = "" + start_minutes;
		}
		start_hours = t.getStart().getHourOfDay();
		discription = t.getDescription();
		task_to_edit = discription + " at " + start_hours + ":"
				+ start_minutesStr + " on " + start_day + "-"
				+ start_month + "-" + start_year;
		return task_to_edit;
	}

	private String getDeadlineDescription(Task t) {
		String discription;
		String task_to_edit;
		int end_day;
		int end_month;
		int end_year;
		int end_minutes;
		int end_hours;
		String end_minutesStr;
		end_day = t.getEnd().getDayOfMonth();
		end_month = t.getEnd().getMonthOfYear();
		end_year = t.getEnd().getYear();
		end_minutes = t.getEnd().getMinuteOfHour();
		if (end_minutes <= 9) {
			end_minutesStr = "0" + end_minutes;
		} else {
			end_minutesStr = "" + end_minutes;
		}
		end_hours = t.getEnd().getHourOfDay();
		discription = t.getDescription();
		task_to_edit = discription + " by " + end_hours + ":"
				+ end_minutesStr + " on " + end_day + "-"
				+ end_month + "-" + end_year;
		return task_to_edit;
	}

	private String getFromTimedDescription(Task t) {
		String discription;
		String task_to_edit;
		int start_day;
		int start_month;
		int start_year;
		int start_minutes;
		int start_hours;
		int end_day;
		int end_month;
		int end_year;
		int end_minutes;
		int end_hours;
		String start_minutesStr;
		String end_minutesStr;
		
		start_day = t.getStart().getDayOfMonth();
		start_month = t.getStart().getMonthOfYear();
		start_year = t.getStart().getYear();
		start_minutes = t.getStart().getMinuteOfHour();
		
		if (start_minutes <= 9) {
			start_minutesStr = "0" + start_minutes;
		} else {
			start_minutesStr = "" + start_minutes;
		}
		
		start_hours = t.getStart().getHourOfDay();
		end_day = t.getEnd().getDayOfMonth();
		end_month = t.getEnd().getMonthOfYear();
		end_year = t.getEnd().getYear();
		end_minutes = t.getEnd().getMinuteOfHour();
		
		if (end_minutes <= 9) {
			end_minutesStr = "0" + end_minutes;
		} else {
			end_minutesStr = "" + end_minutes;
		}
		
		end_hours = t.getEnd().getHourOfDay();
		discription = t.getDescription();
		
		if (start_day == end_day && start_month == end_month
				&& start_year == end_year) {
			task_to_edit = discription + " from " + start_hours
					+ ":" + start_minutesStr + " to " + end_hours
					+ ":" + end_minutesStr + " on " + start_day
					+ "-" + start_month + "-" + start_year;
		} else {
			task_to_edit = discription + " from " + start_hours
					+ ":" + start_minutesStr + " on " + start_day
					+ "-" + start_month + "-" + start_year + " to "
					+ end_hours + ":" + end_minutesStr + " on "
					+ end_day + "-" + end_month + "-" + end_year;
		}
		
		return task_to_edit;
	}
}
